﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows;

namespace SMARTMobile.WinPhone
{
    public class LogService : IApplicationService, ILogger
    {
        private volatile bool _shutdown;
        
        private readonly AutoResetEvent _logEvent = new AutoResetEvent(false);
        private readonly Queue<MessageWrapper> _messages = new Queue<MessageWrapper>();

        private Thread _workerThread;

        public static LogService Current { get; private set; }

        public void StartService(ApplicationServiceContext context)
        {
            Debug.WriteLine("LogService::StartService()");
            Current = this;
            this._workerThread = new Thread(() =>
            {
                Debug.WriteLine("LogService thread started");

                while (!this._shutdown)
                {
                    this._logEvent.WaitOne();

                    if (this._messages.Count > 0)
                    {
                        var wrapper = this._messages.Dequeue();

                        Debug.WriteLine(wrapper.Message);
                    }
                }

                Debug.WriteLine("LogService thread shutting down");
            });

            this._workerThread.Start();
        }

        public void StopService()
        {
            Debug.WriteLine("LogService::StopService()");
            this._shutdown = true;
            this._logEvent.Set();
            this._workerThread.Join(3000);
        }

        private static string FormatString(string format, params object[] args)
        {
            var message = string.Format("Failed to format string: {0}", format);
            try
            {
                message = string.Format(format, args);
            }
            catch (Exception e)
            {
#if DEBUG
                throw;
#endif
            }
            return message;
        }

        public void Information(string message)
        {
            WriteLogEntry(message, TraceEventType.Information);
        }

        public void Information(string format, params object[] args)
        {
            var message = FormatString(format, args);
            Information(message);
        }

        public void Warning(string message)
        {
            WriteLogEntry(message, TraceEventType.Warning);
        }

        public void Warning(string message, Exception e)
        {
            message += "\n" + e;
            Warning(message);
        }

        public void Warning(Exception e, string format, params object[] args)
        {
            var message = FormatString(format, args);
            // TODO: Environment.Hosting.IncludeExceptionDetails
            message += "\n" + e;
            Warning(message);
        }

        public void Error(string message)
        {
            WriteLogEntry(message, TraceEventType.Error);
        }

        public void Error(string message, Exception e)
        {
            message += "\n" + e;
            Error(message);
        }

        public void Error(Exception e, string format, params object[] args)
        {
            var message = FormatString(format, args);
            message += "\n" + e;
            Error(message);
        }

        private void WriteLogEntry(string message, TraceEventType type)
        {
            var wrapper = new MessageWrapper(message, type);
            _messages.Enqueue(wrapper);
            _logEvent.Set();
        }

        private class MessageWrapper
        {
            public MessageWrapper(string message, TraceEventType type)
            {
                Message = message;
                Type = type;
            }

            public string Message { get; private set; }
            public TraceEventType Type { get; private set; }
        }
    }
}
